
#include "imtPublic.h"
#include "imtClient.h"

namespace iMate {

	namespace {
		enum DecryptError
		{
			DE_DataUncomplete = -1,
			DE_DataError = -2
		};

		enum EncryptError
		{
			EE_DataUncomplete = -1,
			EE_DataError = -2
		};
	}

	/**@return   if init success, return true*/
	bool Client::init()
	{
		release();
		mBuffer.reserve(MSG_TEMP_UFFER_INIT_SIZE);
		mP = &mMsgQueueA;
		mV = &mMsgQueueB;
		return true;
	}
	///release everything
	void Client::release()
	{
		disconnect();

		mMsgQueueA.clear();
		mMsgQueueB.clear();
		mP = mV = 0;

		mBuffer.clear();

		SAFE_DELETE(mLock);
		if (mThread)
		{
			IThread::waitAndDestroy(mThread);
			mThread = 0;
		}
	}

	/**connect to server asigned by setServerInfo(@see Client::setServerInfo)
	@return true if connection success
	*/
	bool Client::connect()
	{
		disconnect();

		if (!mThread)
		{
			mThread = IThread::create(make_listener(this, &Client::_netMainRun));
			if (!mThread) return false;
		}
		if (!mLock)
		{
			mLock = ILock::create();
			if (!mLock) return false;
		}

		return mThread->run();
	}
	///disconnect from server
	void Client::disconnect()
	{
		mSocket.close();
		if (mThread)
		{
			mThread->stop();
			mThread->waitForMe();
		}
		mWaitTime =  0;
		mTimer.reset();
	}

	/**get(and pop) a message from receive-message queue
	@buffer buffer to holde message
	@size the size of buffer
	@return the size of data poped
	   @par return ME_DataEmpty if message queue is empty
	        return ME_DataUncomplete if data for a message has not been received completely
			return ME_DataTooBig if the buffer passed in is not enough for current message
			return ME_DataError if the parameters are wrong
	*/
	int Client::popMessage(void* buffer, size_t size)
	{
		//wrong parameters
		if (!buffer || !size) return ME_DataError;

		//empty
		if (mP->empty())
		{
			if (mWaitTime > 0)
			{
				if (mWaitTime > mTimer.getMilliseconds())
				{
					return ME_DataEmpty;
				}
				mWaitTime = 0;
			}

			{
				ScopeLock locker(mLock);
				std::swap(mP, mV);
			}
			setWaitTime(mP->size());
			mTimer.reset();

			return ME_DataEmpty;
		}

		//message unprocessed exist
		MsgData msg;
		MsgQueueResult result = mP->peek(msg);
		if (result == MQR_NoData)
		{
			return ME_DataEmpty;
		}
		if (msg.size > size)
		{
			mP->pop(msg);
			return ME_DataTooBig;
		}
		::memcpy(buffer, msg.data, msg.size);

		mP->pop(msg);
		return (int)msg.size;
	}
	/**push a message to send-message queue
    @param msg a complete message
	@param size the size of message
	@param toSelf send it to server if false, otherwise push it to receive-message 
	   @par queue to simulating a message's arrival from server
	@return the size of data pushed
	   @par return ME_DataEmpty if message queue is empty
	        return ME_DataUncomplete if data for a message has not been received completely
			return ME_DataTooBig if the buffer passed in is not enough for current message
			return ME_DataError if the parameters are wrong
	*/
	int Client::pushMessage(const void* msg, size_t size, bool toSelf)
	{
		if (!msg || !size || size > CmdMaxSize) return ME_DataError;

		//send to self: just put it to mV queue
		if (toSelf)
		{
			ScopeLock locker(mLock);
			if (mV->push(msg, size))
				return (int)size;
			else
				return ME_DataEmpty;
		}

		//send to server: put a header before msg
		char* buffer = mSendBuffer;
		*(int*)buffer = (int)size;
		::memcpy(buffer +MsgHeaderSize, msg, size);
		size += MsgHeaderSize;
		if (encrypt(buffer, size))
			return mSocket.send(buffer, (int)size);

		return ME_DataError;
	}

	/**the main functin of message-receive thread
	@param thread the thread
	@param data the user data
	*/
	void Client::_netMainRun(IThread* thread, UserData& data)
	{
		char buffer[1024 * 10] = {0};
		while (!thread->_needStop())
		{
			int result = mSocket.recv(buffer, sizeof(buffer));
			if (result == 0) continue;
			else if (result == SOCKET_ERROR) break;
			else
				pushData(buffer, (size_t)result);
		}
	}

	/**push a block of data to receive-message queue
	@Note the data pushed is no need to be a single-and-complete application-level message,
	   @par but may be a part of a complete message or composed of serveral messages.
    @param data a bolock of data
	@param size the size of data
	@return a positive number if success, 
	*/
	int Client::pushData(const void* data, size_t size)
	{
		size_t prvSize = mBuffer.size();
		mBuffer.resize(prvSize + size);
		::memcpy(&mBuffer[prvSize], data, size);

		size_t curSize = mBuffer.size();
		char* cur = &mBuffer[0];
		char* msg;
		int result = 1;
		while (true)
		{
			result = decrypt(cur, curSize, &msg);
			if (DE_DataError == result)
			{
				mBuffer.clear();
				return ME_DataError;
			}
			else if (DE_DataUncomplete == result)
			{
				size = mBuffer.size() -curSize;
				mBuffer.erase(mBuffer.begin(), mBuffer.begin() + size);
				return (int)size;
			}
			else
			{
				curSize = curSize - result - (msg -cur);
				cur = msg + result;
				ScopeLock locker(mLock);
				mV->push(msg +MsgHeaderSize, (size_t)result -MsgHeaderSize);
			}
		}
		return result;

	}

	/**when 2 queues are empty, the v-operation thread should wait
	   @par some time and access message queues again. this function
	   @par is to calculate the time according to the message unprocessed number  
	@param the message unprocessed number
	*/
	void Client::setWaitTime(size_t msgCount)
	{
		const ulong MaxCount = 10;
		const ulong SingleElapsed = 20;
		mWaitTime = SingleElapsed * (msgCount > MaxCount ? 0 : MaxCount -(ulong)msgCount);
	}

	/**encrypt one message in data
	@param data data may hold a sequence of messages
	@param size max size of data
	@return the size of the message decrypted if succeeded,  
	   @par DE_DataUncomplete if data is not enough for a single message
	        DE_DataError if no message decrypted
	*/
	int Client::encrypt(char* data, size_t size)
	{
		if (MsgHeaderSize > size) return DE_DataUncomplete;
		//
		//
		return (int)size;
	}
	/**decrypt one message in data
	@param data data may hold a sequence of messages
	@param size max size of data
	@param msgHead return the begin of a message decrypted successfully
	@return the size of the message decrypted if succeeded,  
	   @par DE_DataUncomplete if data is not enough for a single message
	        DE_DataError if no message decrypted
	*/
	int Client::decrypt(char* data, size_t size, char** msgHead)
	{
		if (MsgHeaderSize > size) return DE_DataUncomplete;

		uint32 msgSize = *(uint32*)data;
		if (msgSize > size) return DE_DataError;

		*msgHead = data;
		return msgSize;
	}

}//iMate

